#ifndef __H_THREAD__
#define __H_THREAD__


#include "synchronizer.h"
#include <process.h>

namespace bl
{

class IRunner
{
	friend class Thread;
protected:
	virtual ~IRunner() {}
	virtual void Run() = 0;
};

class Thread
{
public:
	Thread() : thread_(NULL), started_event_(false, false), runner_(NULL), running_func_(NULL) {}
	virtual ~Thread()
	{
		if(thread_)
			::CloseHandle(thread_);
	}

	operator HANDLE() const
	{
		return thread_;
	}
	
	bool Start(IRunner* runner)
	{
		if(runner == NULL)
			return false;

		runner_ = runner;

		if(thread_)
		{
			::CloseHandle(thread_);
			thread_ = NULL;
		}

		unsigned int thread_id;
		thread_ = (HANDLE)_beginthreadex(NULL, 0, _ThreadProc, (LPVOID)this, 0, &thread_id);
		if(!thread_)
			return false;

		started_event_.Wait();
		return true;
	}

	bool Start( void (*runningFunc)())
	{
		if(runningFunc == NULL)
			return false;

		running_func_ = runningFunc;

		if(thread_)
		{
			::CloseHandle(thread_);
			thread_ = NULL;
		}

		unsigned int thread_id;
		thread_ = (HANDLE)_beginthreadex(NULL, 0, _ThreadProc, (LPVOID)this, 0, &thread_id);
		if(!thread_)
			return false;

		started_event_.Wait();
		return true;
	}

	bool WaitUntilExit(unsigned long mSec = INFINITE)
	{
		if(thread_ == NULL)
			return true;

		bool ret = (WAIT_OBJECT_0 == WaitForSingleObject(thread_, mSec));
		if(ret)
		{
			::CloseHandle(thread_);
			thread_ = NULL;
		}
		return ret;
	}

private:
	static unsigned int __stdcall _ThreadProc(void* para)
	{
		Thread* pthis = reinterpret_cast<Thread*>(para);

		pthis->started_event_.Set();

		if(pthis->runner_ != NULL)
			pthis->runner_->Run();
		else if(pthis->running_func_ != NULL)
			pthis->running_func_();

		_endthreadex(0);
		return 0;
	}

	void (*running_func_)();
	IRunner* runner_;
	HANDLE thread_;
	Event started_event_;
};


template<typename T>
class TThread
{
public:
	TThread() : thread_(NULL), started_event_(false, false), the_object_(NULL) {}

	~TThread()
	{
		if(thread_)
			::CloseHandle(thread_);
	}

	operator HANDLE() const
	{
		return thread_;
	}

	bool Start(T* theObject,void (T::*runningFunction)())
	{
		if(thread_)
		{
			::CloseHandle(thread_);
			thread_ = NULL;
		}

		the_object_ = theObject;
		running_fun_ = runningFunction;

		unsigned int thread_id;
		thread_ = (HANDLE)_beginthreadex(NULL, 0, _ThreadProc, (LPVOID)this, 0, &thread_id);
		if(!thread_)
			return false;

		started_event_.Wait();
		return true;
	}

	bool WaitUntilExit(unsigned long mSec = INFINITE)
	{
		if(thread_ == NULL)
			return true;

		bool ret = (WAIT_OBJECT_0 == WaitForSingleObject(thread_, mSec));
		::CloseHandle(thread_);
		thread_ = NULL;
		return ret;
	}

private:
	static unsigned int __stdcall _ThreadProc(void* para)
	{
		TThread* pthis = reinterpret_cast<TThread*>(para);

		pthis->started_event_.Set();

		if(pthis->the_object_!= NULL)
			(pthis->the_object_->*(pthis->running_fun_))();

		_endthreadex(0);
		return 0;
	}

	T* the_object_;
	void (T::*running_fun_)();
	HANDLE thread_;
	Event started_event_;
};


}



#endif